#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
直接数据保存器
监听Arduino串口数据并直接保存到文件
避免端口占用问题的解决方案
"""

import serial
import serial.tools.list_ports
import json
import time
import os
from datetime import datetime
import threading
import queue

class DirectDataSaver:
    def __init__(self):
        self.serial_port = None
        self.is_running = False
        self.data_queue = queue.Queue()
        # 文件路径配置 - 修改为相对于当前目录的路径
        self.api_folder = os.path.join(os.path.dirname(__file__), "..", "src", "api")
        self.public_folder = os.path.dirname(__file__)  # 当前目录就是public文件夹
        os.makedirs(self.api_folder, exist_ok=True)
        os.makedirs(self.public_folder, exist_ok=True)
        self.output_file = os.path.join(self.api_folder, "sensor_data.txt")
        self.json_file = os.path.join(self.public_folder, "sensor_data.json")
        
        # 文件保存命令状态
        self.file_save_mode = False
        self.file_save_path = None
        self.file_save_data = []
        
        # 确保输出目录存在
        os.makedirs(os.path.dirname(self.output_file), exist_ok=True)
        
    def find_arduino_port(self):
        """查找Arduino端口"""
        print("正在搜索可用串口...")
        ports = serial.tools.list_ports.comports()
        
        # 优先尝试COM3
        for port in ports:
            if port.device == 'COM3':
                print(f"找到目标端口: {port.device} - {port.description}")
                return port.device
        
        # 查找Arduino相关端口
        arduino_keywords = ['Arduino', 'USB Serial', 'CH340', 'CP210', 'FTDI']
        for port in ports:
            for keyword in arduino_keywords:
                if keyword.lower() in port.description.lower():
                    print(f"找到Arduino端口: {port.device} - {port.description}")
                    return port.device
        
        # 显示所有可用端口
        print("\n所有可用端口:")
        for port in ports:
            print(f"  {port.device}: {port.description}")
        
        return None
    
    def connect_serial(self):
        """连接串口"""
        port_name = self.find_arduino_port()
        if not port_name:
            print("错误：未找到Arduino端口")
            return False
        
        try:
            self.serial_port = serial.Serial(
                port=port_name,
                baudrate=115200,
                timeout=1,
                write_timeout=1
            )
            print(f"成功连接到端口: {port_name}")
            time.sleep(2)  # 等待Arduino重启
            return True
        except serial.SerialException as e:
            print(f"串口连接失败: {e}")
            return False
    
    def save_data_to_file(self, data_line):
        """保存数据到文件"""
        timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f")[:-3]
        
        # 处理Arduino文件保存命令
        if "FILE_SAVE_START" in data_line:
            print(f"检测到FILE_SAVE_START命令: {data_line.strip()}")
            self.file_save_mode = True
            self.file_save_data = []
            return
        elif "FILE_SAVE_END" in data_line:
            print(f"检测到FILE_SAVE_END命令: {data_line.strip()}")
            if self.file_save_mode and self.file_save_data:
                print(f"准备保存JSON数据到: {self.file_save_path}")
                # 处理保存的JSON数据
                for json_line in self.file_save_data:
                    if json_line.strip().startswith('{') and json_line.strip().endswith('}'):
                        print(f"JSON数据内容: {json_line.strip()}")
                        self.save_json_data(json_line.strip(), timestamp)
            else:
                print(f"保存条件不满足 - mode:{self.file_save_mode}, path:{self.file_save_path}, data:{bool(self.file_save_data)}")
            self.file_save_mode = False
            self.file_save_path = None
            self.file_save_data = []
            return
        elif self.file_save_mode:
            if self.file_save_path is None:
                self.file_save_path = data_line.strip()
                print(f"设置保存路径: {self.file_save_path}")
            else:
                self.file_save_data.append(data_line)
                print(f"接收JSON数据: {data_line.strip()}")
            return
        
        # 保存原始数据到txt文件
        with open(self.output_file, 'a', encoding='utf-8') as f:
            f.write(f"[{timestamp}] {data_line}\n")
        
        # 如果是JSON数据，也保存到JSON文件
        if data_line.strip().startswith('{') and data_line.strip().endswith('}'):
            self.save_json_data(data_line.strip(), timestamp)
    
    def save_json_data(self, json_str, timestamp):
        """保存JSON数据到文件"""
        try:
            # 解析JSON字符串
            data = json.loads(json_str)
            
            # 优化数据结构
            optimized_data = {
                'id': len(self.get_existing_data()) + 1,
                'timestamp': data.get('timestamp', 0),
                'pc_timestamp': timestamp,
                'connection': {
                    'uno_connected': data.get('uno_connected', False),
                    'status': 'online' if data.get('uno_connected', False) else 'offline'
                },
                'pressure': {
                    'raw': data.get('pressure_raw', 0),
                    'voltage': round(data.get('pressure_voltage', 0.0), 2),
                    'mapped': data.get('pressure_mapped', 0),
                    'percentage': round((data.get('pressure_mapped', 0) / 100) * 100, 1) if data.get('pressure_mapped', 0) > 0 else 0
                },
                'tiles': {
                    'active_count': sum(1 for x in data.get('tile_data', []) if x > 0),
                    'max_pressure': max(data.get('tile_data', [0])),
                    'total_pressure': sum(data.get('tile_data', [])),
                    'data': data.get('tile_data', [])
                },
                'summary': {
                    'has_pressure': data.get('pressure_mapped', 0) > 0,
                    'active_tiles': sum(1 for x in data.get('tile_data', []) if x > 0),
                    'status_text': self.get_status_text(data)
                }
            }
            
            # 读取现有数据
            existing_data = self.get_existing_data()
            
            # 添加新数据
            existing_data.append(optimized_data)
            
            # 保持最近500条记录（优化后的数据更大）
            if len(existing_data) > 500:
                existing_data = existing_data[-500:]
            
            # 保存到文件
            with open(self.json_file, 'w', encoding='utf-8') as f:
                json.dump({
                    'metadata': {
                        'total_records': len(existing_data),
                        'last_updated': timestamp,
                        'data_version': '2.0',
                        'description': 'Smart Floor Sensor Data - Optimized Format'
                    },
                    'data': existing_data
                }, f, ensure_ascii=False, indent=2)
            
            print(f"优化JSON数据已保存: ID={optimized_data['id']}, 压力={optimized_data['pressure']['mapped']}, 活跃地砖={optimized_data['tiles']['active_count']}")
            
        except json.JSONDecodeError as e:
            print(f"JSON解析错误: {e}")
        except Exception as e:
            print(f"保存JSON数据时出错: {e}")
    
    def get_existing_data(self):
        """获取现有数据"""
        if os.path.exists(self.json_file):
            try:
                with open(self.json_file, 'r', encoding='utf-8') as f:
                    file_data = json.load(f)
                    # 兼容新旧格式
                    if isinstance(file_data, dict) and 'data' in file_data:
                        return file_data['data']
                    elif isinstance(file_data, list):
                        return file_data
            except (json.JSONDecodeError, FileNotFoundError):
                pass
        return []
    
    def get_status_text(self, data):
        """生成状态描述文本"""
        if not data.get('uno_connected', False):
            return '设备离线'
        
        pressure = data.get('pressure_mapped', 0)
        active_tiles = sum(1 for x in data.get('tile_data', []) if x > 0)
        
        if pressure == 0 and active_tiles == 0:
            return '无压力检测'
        elif pressure > 80:
            return f'高压力检测 ({pressure}%)'
        elif pressure > 50:
            return f'中等压力检测 ({pressure}%)'
        elif pressure > 0:
            return f'轻微压力检测 ({pressure}%)'
        else:
            return '待机状态'
    
    def data_reader_thread(self):
        """数据读取线程"""
        buffer = ""
        
        while self.is_running:
            try:
                if self.serial_port and self.serial_port.in_waiting > 0:
                    data = self.serial_port.read(self.serial_port.in_waiting).decode('utf-8', errors='ignore')
                    buffer += data
                    
                    # 处理完整的行
                    while '\n' in buffer:
                        line, buffer = buffer.split('\n', 1)
                        line = line.strip()
                        if line:
                            self.data_queue.put(line)
                            
                time.sleep(0.01)  # 避免CPU占用过高
                
            except Exception as e:
                print(f"数据读取错误: {e}")
                time.sleep(1)
    
    def data_saver_thread(self):
        """数据保存线程"""
        while self.is_running:
            try:
                # 等待数据，超时1秒
                data_line = self.data_queue.get(timeout=1)
                self.save_data_to_file(data_line)
                print(f"已保存: {data_line[:100]}{'...' if len(data_line) > 100 else ''}")
                
            except queue.Empty:
                continue
            except Exception as e:
                print(f"数据保存错误: {e}")
    
    def start(self):
        """启动数据保存器"""
        print("=== Arduino直接数据保存器 ===")
        print(f"输出文件: {self.output_file}")
        print(f"JSON文件: {self.json_file}")
        
        if not self.connect_serial():
            return False
        
        self.is_running = True
        
        # 启动线程
        reader_thread = threading.Thread(target=self.data_reader_thread, daemon=True)
        saver_thread = threading.Thread(target=self.data_saver_thread, daemon=True)
        
        reader_thread.start()
        saver_thread.start()
        
        print("数据保存器已启动，按Ctrl+C停止...")
        
        try:
            while self.is_running:
                time.sleep(1)
        except KeyboardInterrupt:
            print("\n正在停止数据保存器...")
            self.stop()
        
        return True
    
    def stop(self):
        """停止数据保存器"""
        self.is_running = False
        if self.serial_port:
            self.serial_port.close()
        print("数据保存器已停止")

if __name__ == "__main__":
    saver = DirectDataSaver()
    saver.start()